///PHILOTES Source Code.  (C)2012 PhiloLabs

#include "OgreStableHeaders.h"

#include "OgreRenderSystemCapabilities.h"
#include "core/logManager.h"
#include "OgreStringConverter.h"
#include "core/exception.h"

namespace Ogre {

	//-----------------------------------------------------------------------
	RenderSystemCapabilities::RenderSystemCapabilities()
		: mVendor(GPU_UNKNOWN)
		, mNumWorldMatrices(0)
		, mNumTextureUnits(0)
		, mStencilBufferBitDepth(0)
		, mNumVertexBlendMatrices(0)
		, mNumMultiRenderTargets(1)
		, mNonPOW2TexturesLimited(false)
	{

		for(int i = 0; i < CAPS_CATEGORY_COUNT; i++)
		{
			mCapabilities[i] = 0;
		}
		mCategoryRelevant[CAPS_CATEGORY_COMMON] = true;
		mCategoryRelevant[CAPS_CATEGORY_COMMON_2] = true;
		// each rendersystem should enable these
		mCategoryRelevant[CAPS_CATEGORY_D3D9] = false;
		mCategoryRelevant[CAPS_CATEGORY_GL] = false;


	}
	//-----------------------------------------------------------------------
	RenderSystemCapabilities::~RenderSystemCapabilities()
	{
	}
	//-----------------------------------------------------------------------
	void RenderSystemCapabilities::log(Core::Log* pLog)
	{
		pLog->logMessage("RenderSystem capabilities");
		pLog->logMessage("-------------------------");
		pLog->stream().write("RenderSystem Name: %s", getRenderSystemName().c_str() );
		pLog->stream().write("GPU Vendor: %s", vendorToString(getVendor()).c_str() );
		pLog->stream().write("Device Name: %s", getDeviceName().c_str() );
		pLog->stream().write("Driver Version: %s", getDriverVersion().toString().c_str() );
		pLog->stream().write(" * Fixed function pipeline: %s", Util::String::FromBool( hasCapability(RSC_FIXED_FUNCTION) ).AsCharPtr() );
		pLog->stream().write(" * Hardware generation of mipmaps: %s", Util::String::FromBool( hasCapability(RSC_AUTOMIPMAP) ).AsCharPtr() );
		pLog->stream().write(" * Texture blending: %s", Util::String::FromBool( hasCapability(RSC_BLENDING) ).AsCharPtr() );
		pLog->stream().write(" * Anisotropic texture filtering: %s", Util::String::FromBool( hasCapability(RSC_ANISOTROPY) ).AsCharPtr() );
		pLog->stream().write(" * Dot product texture operation: %s", Util::String::FromBool( hasCapability(RSC_DOT3) ).AsCharPtr() );
		pLog->stream().write(" * Cube mapping: %s", Util::String::FromBool( hasCapability(RSC_CUBEMAPPING) ).AsCharPtr() );
		pLog->stream().write(" * Hardware stencil buffer: %s", Util::String::FromBool( hasCapability(RSC_HWSTENCIL) ).AsCharPtr() );

		if (hasCapability(RSC_HWSTENCIL))
		{
			pLog->stream().write("   - Stencil depth: %u", getStencilBufferBitDepth() );
			pLog->stream().write("   - Two sided stencil support: %s", Util::String::FromBool( hasCapability(RSC_TWO_SIDED_STENCIL) ).AsCharPtr() );
			pLog->stream().write("   - Wrap stencil values: %s", Util::String::FromBool( hasCapability(RSC_STENCIL_WRAP) ).AsCharPtr() );
		}

		pLog->stream().write(" * Hardware vertex / index buffers: %s", Util::String::FromBool( hasCapability(RSC_VBO) ).AsCharPtr() );
		pLog->stream().write(" * Vertex programs: %s", Util::String::FromBool( hasCapability(RSC_VERTEX_PROGRAM) ).AsCharPtr() );
		pLog->stream().write(" * Number of floating-point constants for vertex programs: %u", mVertexProgramConstantFloatCount );
		pLog->stream().write(" * Number of integer constants for vertex programs: %u", mVertexProgramConstantIntCount );
		pLog->stream().write(" * Number of boolean constants for vertex programs: %u", mVertexProgramConstantBoolCount );

		pLog->stream().write(" * Fragment programs: %s", Util::String::FromBool( hasCapability(RSC_FRAGMENT_PROGRAM) ).AsCharPtr() );
		pLog->stream().write(" * Number of floating-point constants for fragment programs: %u", mFragmentProgramConstantFloatCount );
		pLog->stream().write(" * Number of integer constants for fragment programs: %u", mFragmentProgramConstantIntCount );
		pLog->stream().write(" * Number of boolean constants for fragment programs: %u", mFragmentProgramConstantBoolCount );

		pLog->stream().write(" * Geometry programs: %s", Util::String::FromBool( hasCapability(RSC_GEOMETRY_PROGRAM) ).AsCharPtr() );
		pLog->stream().write(" * Number of floating-point constants for geometry programs: %u", mGeometryProgramConstantFloatCount );
		pLog->stream().write(" * Number of integer constants for geometry programs: %u", mGeometryProgramConstantIntCount );
		pLog->stream().write(" * Number of boolean constants for geometry programs: %u", mGeometryProgramConstantBoolCount );

		String profileList = "";
		for(ShaderProfiles::iterator iter = mSupportedShaderProfiles.begin(), end = mSupportedShaderProfiles.end();
			iter != end; ++iter)
		{
			profileList += " " + *iter;
		}

		pLog->stream().write(" * Supported Shader Profiles:%s", profileList.c_str() );

		pLog->stream().write(" * Texture Compression: %s", Util::String::FromBool( hasCapability(RSC_TEXTURE_COMPRESSION) ).AsCharPtr() );

		if (hasCapability(RSC_TEXTURE_COMPRESSION))
		{
			pLog->stream().write("   - DXT: %s", Util::String::FromBool( hasCapability(RSC_TEXTURE_COMPRESSION_DXT) ).AsCharPtr() );
			pLog->stream().write("   - VTC: %s", Util::String::FromBool( hasCapability(RSC_TEXTURE_COMPRESSION_VTC) ).AsCharPtr() );
			pLog->stream().write("   - PVRTC: %s", Util::String::FromBool( hasCapability(RSC_TEXTURE_COMPRESSION_PVRTC) ).AsCharPtr() );

		}

		pLog->stream().write(" * Scissor Rectangle: %s", Util::String::FromBool( hasCapability(RSC_SCISSOR_TEST) ).AsCharPtr() );
		pLog->stream().write(" * Hardware Occlusion Query: %s", Util::String::FromBool( hasCapability(RSC_HWOCCLUSION) ).AsCharPtr() );
		pLog->stream().write(" * User clip planes: %s", Util::String::FromBool( hasCapability(RSC_USER_CLIP_PLANES) ).AsCharPtr() );
		pLog->stream().write(" * VET_UBYTE4 vertex element type: %s", Util::String::FromBool( hasCapability(RSC_VERTEX_FORMAT_UBYTE4) ).AsCharPtr() );
		pLog->stream().write(" * Infinite far plane projection: %s", Util::String::FromBool( hasCapability(RSC_INFINITE_FAR_PLANE) ).AsCharPtr() );
		pLog->stream().write(" * Hardware render-to-texture: %s", Util::String::FromBool( hasCapability(RSC_HWRENDER_TO_TEXTURE) ).AsCharPtr() );
		pLog->stream().write(" * Floating point textures: %s", Util::String::FromBool( hasCapability(RSC_TEXTURE_FLOAT) ).AsCharPtr() );
		pLog->stream().write(" * Non-power-of-two textures: %s%s", Util::String::FromBool( hasCapability(RSC_NON_POWER_OF_2_TEXTURES) ).AsCharPtr(), (mNonPOW2TexturesLimited ? " (limited)" : "") );
		pLog->stream().write(" * Volume textures: %s", Util::String::FromBool( hasCapability(RSC_TEXTURE_3D) ).AsCharPtr() );
		pLog->stream().write(" * Multiple Render Targets: %u", mNumMultiRenderTargets );
		pLog->stream().write("   - With different bit depths: %s", Util::String::FromBool( hasCapability(RSC_MRT_DIFFERENT_BIT_DEPTHS) ).AsCharPtr() );
		pLog->stream().write(" * Point Sprites: %s", Util::String::FromBool( hasCapability(RSC_POINT_SPRITES) ).AsCharPtr() );
		pLog->stream().write(" * Extended point parameters: %s", Util::String::FromBool( hasCapability(RSC_POINT_EXTENDED_PARAMETERS) ).AsCharPtr() );

		if(hasCapability(RSC_POINT_SPRITES))
		{
			pLog->stream().write(" * Max Point Size: %f", mMaxPointSize );
		}

		pLog->stream().write(" * Vertex texture fetch: %s", Util::String::FromBool( hasCapability(RSC_VERTEX_TEXTURE_FETCH) ).AsCharPtr() );
		pLog->stream().write(" * Number of world matrices: %u", mNumWorldMatrices );
		pLog->stream().write(" * Number of texture units: %u", mNumTextureUnits );
		pLog->stream().write(" * Stencil buffer depth: %u", mStencilBufferBitDepth );
		pLog->stream().write(" * Number of vertex blend matrices: %u", mNumVertexBlendMatrices );

		if (hasCapability(RSC_VERTEX_TEXTURE_FETCH))
		{
			pLog->stream().write("   - Max vertex textures: %u", mNumVertexTextureUnits );
			pLog->stream().write("   - Vertex textures shared: %s", Util::String::FromBool(mVertexTextureUnitsShared).AsCharPtr() );
		}

		pLog->stream().write(" * Render to Vertex Buffer : %s", Util::String::FromBool( hasCapability(RSC_HWRENDER_TO_VERTEX_BUFFER) ).AsCharPtr() );

		if (mCategoryRelevant[CAPS_CATEGORY_GL])
		{
			pLog->stream().write(" * GL 1.5 without VBO workaround: %s", Util::String::FromBool( hasCapability(RSC_GL1_5_NOVBO) ).AsCharPtr() );
			pLog->stream().write(" * Frame Buffer objects: %s", Util::String::FromBool( hasCapability(RSC_FBO) ).AsCharPtr() );
			pLog->stream().write(" * Frame Buffer objects (ARB extension): %s", Util::String::FromBool( hasCapability(RSC_FBO_ARB) ).AsCharPtr() );
			pLog->stream().write(" * Frame Buffer objects (ATI extension): %s", Util::String::FromBool( hasCapability(RSC_FBO_ATI) ).AsCharPtr() );
			pLog->stream().write(" * PBuffer support: %s", Util::String::FromBool( hasCapability(RSC_PBUFFER) ).AsCharPtr() );
			pLog->stream().write(" * GL 1.5 without HW-occlusion workaround: %s", Util::String::FromBool( hasCapability(RSC_GL1_5_NOHWOCCLUSION) ).AsCharPtr() );
			pLog->stream().write(" * Separate shader objects: %s", Util::String::FromBool( hasCapability(RSC_SEPARATE_SHADER_OBJECTS) ).AsCharPtr() );
		}

		if (mCategoryRelevant[CAPS_CATEGORY_D3D9])
		{
			pLog->stream().write(" * DirectX per stage constants: %s", Util::String::FromBool( hasCapability(RSC_PERSTAGECONSTANT) ).AsCharPtr() );
		}

	}
	//---------------------------------------------------------------------
	StringVector RenderSystemCapabilities::msGPUVendorStrings;
	//---------------------------------------------------------------------
	GPUVendor RenderSystemCapabilities::vendorFromString(const String& vendorString)
	{
		initVendorStrings();
		GPUVendor ret = GPU_UNKNOWN;
		String cmpString = vendorString;
		StringUtil::toLowerCase(cmpString);
		for (int i = 0; i < GPU_VENDOR_COUNT; ++i)
		{
			// case insensitive (lower case)
			if (msGPUVendorStrings[i] == cmpString)
			{
				ret = static_cast<GPUVendor>(i);
				break;
			}
		}

		return ret;
		
	}
	//---------------------------------------------------------------------
	String RenderSystemCapabilities::vendorToString(GPUVendor v)
	{
		initVendorStrings();
		return msGPUVendorStrings[v];
	}
	//---------------------------------------------------------------------
	void RenderSystemCapabilities::initVendorStrings()
	{
		if (msGPUVendorStrings.empty())
		{
			// Always lower case!
			msGPUVendorStrings.resize(GPU_VENDOR_COUNT);
			msGPUVendorStrings[GPU_UNKNOWN] = "unknown";
			msGPUVendorStrings[GPU_NVIDIA] = "nvidia";
			msGPUVendorStrings[GPU_ATI] = "ati";
			msGPUVendorStrings[GPU_INTEL] = "intel";
			msGPUVendorStrings[GPU_3DLABS] = "3dlabs";
			msGPUVendorStrings[GPU_S3] = "s3";
			msGPUVendorStrings[GPU_MATROX] = "matrox";
			msGPUVendorStrings[GPU_SIS] = "sis";
			msGPUVendorStrings[GPU_IMAGINATION_TECHNOLOGIES] = "imagination technologies";
			msGPUVendorStrings[GPU_APPLE] = "apple";    // iPhone Simulator
		}
	}

}
